package po.tree;

import po.entity.Indicator;
import po.entity.Param;
import po.entity.ParamValue;
import po.enums.Operator;
import po.utils.ParamValueHandler;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created by guowei on 2019/6/9.
 */
public class ResultNode {
    private Position position;
    private Map<String, Param> params;
    private Map<String, Indicator> indicators;

    public ResultNode(Position position) {
        this.position = position;
        this.params = new HashMap<>();
        this.indicators = new HashMap<>();
    }

    public void calculate(ResultNode rn, Operator operator) throws Exception {
        this.mergeIndicators(rn.getIndicators(), operator);
        this.mergeParams(rn.getParams(), operator);
    }

    public Map<String, Param> getParams() {
        return params;
    }

    public Map<String, Indicator> getIndicators() {
        return indicators;
    }

    public Position getPosition() {
        return position;
    }

    // add or update
    public void putParam(Param p) {
        this.params.put(p.getID(), p);
    }

    // add or update
    public void putIndicator(Indicator i) {
        this.indicators.put(i.getID(), i);
    }

    public void dump(ResultNode rn) {
        Set<Map.Entry<String, Param>> paramsEntrySet = rn.getParams().entrySet();
        for (Map.Entry<String, Param> entry: paramsEntrySet) {
            this.params.put(entry.getKey(), entry.getValue());
        }
        Set<Map.Entry<String, Indicator>> indicatorsEntrySet = rn.getIndicators().entrySet();
        for (Map.Entry<String, Indicator> entry: indicatorsEntrySet) {
            this.indicators.put(entry.getKey(), entry.getValue());
        }
    }

    private void mergeIndicators(Map<String, Indicator> outerIndicators, Operator operator) throws Exception{
        Set<Map.Entry<String, Indicator>> indicatorSet = outerIndicators.entrySet();
        for(Map.Entry<String, Indicator> entry : indicatorSet) {
            if(!this.indicators.containsKey(entry.getKey())) {
                this.indicators.put(entry.getKey(), entry.getValue());
            } else {
                // meet same indicator
                Indicator selfIndicator = this.indicators.get(entry.getKey());
                ParamValue newParamvalue = ParamValueHandler.checkParamValueCompatible(
                        selfIndicator.getValue(), entry.getValue().getValue(), operator);
                selfIndicator.setValue(newParamvalue);
                this.putIndicator(selfIndicator);
            }
        }
    }

    private void mergeParams(Map<String, Param> outerParams, Operator operator) throws Exception{
        Set<Map.Entry<String, Param>> paramSet = outerParams.entrySet();
        for(Map.Entry<String, Param> entry : paramSet) {
            if(!this.params.containsKey(entry.getKey())) {
                this.params.put(entry.getKey(), entry.getValue());
            } else {
                // meet same Param
                Param selfParam = this.params.get(entry.getKey());
                ParamValue newParamvalue = ParamValueHandler.checkParamValueCompatible(
                        selfParam.getValue(), entry.getValue().getValue(), operator);
                selfParam.setValue(newParamvalue);
                this.putParam(selfParam);
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("RESULT-NODE VALUE: \n");
        sb.append("--- Indicators ---\n");
        Set<Map.Entry<String, Indicator>> indicatorSet = this.indicators.entrySet();
        for(Map.Entry<String, Indicator> entry : indicatorSet) {
            sb.append("{key: ");
            sb.append(entry.getKey());
            sb.append(", value: ");
            sb.append(entry.getValue().toString());
            sb.append("}\n");
        }
        sb.append("--- Params ---\n");
        Set<Map.Entry<String, Param>> paramSet = this.params.entrySet();
        for(Map.Entry<String, Param> entry : paramSet) {
            sb.append("{key: ");
            sb.append(entry.getKey());
            sb.append(", value: ");
            sb.append(entry.getValue().toString());
            sb.append("}\n");
        }
        return sb.toString();
    }
}
